﻿// Partial Copyright (c) LogoUI Software Solutions LTD
// Autor: Vladislav Spivak
// This source file is the part of LogoFX Framework http://logofx.codeplex.com
// See accompanying licences and credits.

using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Collections
{
    public static class Extensions
    {
        /// <summary>
        /// Beautifies the string.
        /// </summary>
        /// <param name="originalName">original string.</param>
        /// <returns></returns>
        public static string Beautify(this IEnumerable<char> originalName)
        {
            StringBuilder word = new StringBuilder();
            List<string> words = new List<string>();
            foreach (char c in originalName)
            {
                // ignore punctuations and blanks
                if (Char.IsLetterOrDigit(c))
                {
                    if (Char.IsUpper(c))
                    {
                        if (word.Length > 0 && Char.IsLower(word[word.Length - 1]))
                        {
                            words.Add(word.ToString());
                            word = new StringBuilder();
                        }
                    }
                    word.Append(c);
                }
            }
            if (word.Length > 0)
                words.Add(word.ToString());
            String beautifiedName = String.Join(" ", words.ToArray());
            return beautifiedName;
        }

        #region Fast Iterators (applying operation to elements one by one and yielding them further)
        /// <summary>
        /// Same as apply
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie">The ie.</param>
        /// <param name="action">The action.</param>
        public static IEnumerable<T> ForEachByOne<T>(this IEnumerable<T> ie, Action<T> action)
        {
            using (IEnumerator<T> enumerator = ie.GetEnumerator())
            {
                //enumerator.Reset();
                while (enumerator.MoveNext())
                {
                    action(enumerator.Current);
                    yield return enumerator.Current;
                }
            }
        }

        /// <summary>
        /// Performs an action on each item and increases an index of the enumerable.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie"></param>
        /// <param name="action"></param>
        public static IEnumerable<T> ForEachByOne<T>(this IEnumerable<T> ie, Action<T, int> action)
        {
            int index = 0;
            using (IEnumerator<T> enumerator = ie.GetEnumerator())
            {
                //enumerator.Reset();
                while (enumerator.MoveNext())
                {
                    action(enumerator.Current, index++);
                    yield return enumerator.Current;
                }
            }
        }


        /// <summary>
        /// Performs an action on range of elements and increases an index of the enumerable.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="action"></param>
        public static IEnumerable<T> ForEachByOne<T>(this IEnumerable<T> ie, int start, int end, Action<T, int> action)
        {
            return ie.Skip(start + 1)
                .Take(end - start)
                .ForEachByOne(action);            
        }

        /// <summary>
        ///  Performs the specified action on each element of the <see cref="IEnumerable{T}"/> 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie"></param>
        /// <param name="action">The <see cref="System.Action()"/> delegate to perform on each element of the <see cref="IEnumerable{T}"/>.</param>
        /// <exception cref="System.ArgumentNullException">action is <see langword="null"/>.</exception>        
        public static IEnumerable<T> ApplyByOne<T>(this IEnumerable<T> ie, Action<T> action)
        {
            return ie.ForEachByOne(action);
        }

        //todo:be carefull, stack overflow
        public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
        {
            //IEnumerable<T> result =
            // source.Concat(source.SelectMany(fnRecurse).Traverse(fnRecurse));
            //return result;
            foreach (T item in source)
            {
                yield return item;

                IEnumerable<T> seqRecurse = fnRecurse(item);

                if (seqRecurse != null)
                {
                    foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
                    {
                        yield return itemRecurse;
                    }
                }
            }
        }

        #endregion

        #region Batch Iterators (applying operation to all elements in one batch)
        /// <summary>
        /// Same as apply
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie">The ie.</param>
        /// <param name="action">The action.</param>
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> ie, Action<T> action)
        {
            using (IEnumerator<T> enumerator = ie.GetEnumerator())
            {
                //enumerator.Reset();
                while (enumerator.MoveNext())
                {
                    action(enumerator.Current);
                }
            }
            return ie;
        }

        /// <summary>
        /// Performs an action on each item and increases an index of the enumerable.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie"></param>
        /// <param name="action"></param>
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> ie, Action<T, int> action)
        {
            int index = 0;
            using (IEnumerator<T> enumerator = ie.GetEnumerator())
            {
                //enumerator.Reset();
                while (enumerator.MoveNext())
                {
                    action(enumerator.Current, index++);
                }
            }
            return ie;
        }


        /// <summary>
        /// Performs an action on range of elements and increases an index of the enumerable.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="action"></param>
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> ie, int start, int end, Action<T, int> action)
        {
            return ie.Skip(start + 1)
                .Take(end - start)
                .ForEach(action);
        }


        /// <summary>
        ///  Performs the specified action on each element of the <see cref="IEnumerable{T}"/> 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ie"></param>
        /// <param name="action">The <see cref="System.Action()"/> delegate to perform on each element of the <see cref="IEnumerable{T}"/>.</param>
        /// <exception cref="System.ArgumentNullException">action is <see langword="null"/>.</exception>
        /// <remarks>compatibility stuff for Caliburn.Micro</remarks>
        public static IEnumerable<T> Apply<T>(this IEnumerable<T> ie, Action<T> action)
        {
            return ie.ForEach(action);
        }

        #endregion

    }
}
